//! A set of functions to create packets for testing the NIC transmission functionality.

use super::{get_ixgbe_nic, TransmitBuffer};
use pci::PciLocation;
use net::NetworkDevice;

/// Sends a dhcp request packet on the ixgbe NIC.
pub fn dhcp_request_packet(nic_id: PciLocation) -> Result<(), &'static str> {
    let transmit_buffer = create_dhcp_test_packet()?;
    let ixgbe_nic = get_ixgbe_nic(nic_id)?;
    ixgbe_nic.lock().send(transmit_buffer);
    Ok(())
}

/// Creates a `TransmitBuffer` containing a dhcp packet.
pub fn create_dhcp_test_packet() -> Result<TransmitBuffer, &'static str> {
    let packet: [u8; 314] = [
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1f, 0xc6, 0x9c, 0x89, 0x4c, 0x08, 0x00, 0x45,
        0x00, 0x01, 0x2c, 0xa8, 0x36, 0x00, 0x00, 0xfa, 0x11, 0x17, 0x8b, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0x00, 0x44, 0x00, 0x43, 0x01, 0x18, 0x59, 0x1f, 0x01, 0x01, 0x06,
        0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xc6, 0x9c, 0x89,
        0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x01,
        0x3d, 0x07, 0x01, 0x00, 0x1f, 0xc6, 0x9c, 0x89, 0x4c, 0x32, 0x04, 0x00, 0x00, 0x00, 0x00,
        0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    ];
    let mut transmit_buffer = TransmitBuffer::new(packet.len() as u16)?;
    { 
        let buffer = &mut transmit_buffer;
        buffer.copy_from_slice(&packet);
    }
    Ok(transmit_buffer)
}

/// Creates a `TransmitBuffer` that contains a packet with only the ethernet header.
pub fn create_raw_packet(
    dest_mac_address: &[u8], 
    source_mac_address: &[u8], 
    message: &[u8]
) -> Result<TransmitBuffer, &'static str> {
    
    const ETHER_TYPE_LEN: usize = 2;
    const MAC_ADDR_LEN: usize = 6;
    const ETHERNET_HEADER_LEN: usize = MAC_ADDR_LEN * 2 + ETHER_TYPE_LEN;
    const MIN_PACKET_LEN: usize = 46;

    let mut len = message.len() as u16;
    if len > 1500 {
        return Err("Too long for a raw packet");
    }
    if len < MIN_PACKET_LEN as u16 {
        len = 46;
    }

    let ether_type: [u8; ETHER_TYPE_LEN] = [(len >> 8) as u8, len as u8];

    let mut transmit_buffer = TransmitBuffer::new(ETHERNET_HEADER_LEN as u16 + len)?;
    let buffer = &mut transmit_buffer;
    buffer[0..MAC_ADDR_LEN].copy_from_slice(dest_mac_address);
    buffer[6..(6 + MAC_ADDR_LEN)].copy_from_slice(source_mac_address);
    buffer[12..(12 + ETHER_TYPE_LEN)].copy_from_slice(&ether_type);
    buffer[14..(14 + message.len())].copy_from_slice(message);

    Ok(transmit_buffer)
}
